// TGF Gunner - Ported by Glaber (original LUA) and MIDIMan (TGF movement)

freeslot(
	"MT_TGFGUNNER", 
	"S_TGFGUNER_TGFMOVEMENT",
	"S_TGFGUNER_SHOOT",
	"S_TGFGUNERLOOK1",
	"S_TGFGUNERLOOK2",
	"S_TGFGUNERZOOM1",
	"S_TGFGUNERZOOM2",
	"S_TGFGUNERSHOOT1",
	"S_TGFGUNERSHOOT2",
	"SPR_TFBT"
)

local function CustomTGFGunnerSpawn(mobj, mapthing)
	if not (mobj and mobj.valid
	and mapthing and mapthing.valid)
		return
	end
	
    local offset
    
    if mapthing.z != 0
        offset = mapthing.z
    else
        offset = 33*FRACUNIT
    end
    
    if (mapthing.options & MTF_OBJECTFLIP)
        if offset != 0
            mobj.z = mobj.z - offset
        else
            mobj.z = mobj.ceilingz
        end
    else
        if offset != 0
            mobj.z = mobj.z + offset
        else
            mobj.z = mobj.floorz
        end
    end
    
end

addHook("MapThingSpawn", function(mo, mthing)
	if not (mo and mo.valid
	and mthing and mthing.valid)
		return
	end
	
	CustomTGFGunnerSpawn(mo, mthing)
	
	if (mthing.options & MTF_OBJECTSPECIAL)
		mo.state = S_TGFGUNER_TGFMOVEMENT
		mo.flags = $|MF_NOCLIPHEIGHT
		mo.tgfMovement = true
		mo.tgfAngle = FixedAngle(mthing.angle*FRACUNIT)
	end
end, MT_TGFGUNNER)

// Quick function to check if the nextPosition goes over the endPosition
local function TGFDifferenceCheck(diff, nextPos, endPos)
	if abs(diff) >= abs(endPos - nextPos)
	or 3*abs(diff)/2 >= abs(endPos - nextPos)
		return endPos
	end
	
	return nextPos + diff
end

local function TGFMovement(mo, endPos, speed)
	if not (mo and mo.valid) then return end
	if not endPos then return end
	if not speed then return end
	
	local nextPos = {x = mo.x, y = mo.y, z = mo.z}
	
	local angle = R_PointToAngle2(mo.x, mo.y, endPos.x, endPos.y)
	local dist = FixedHypot(endPos.x - mo.x, endPos.y - mo.y)
	local pitch = R_PointToAngle2(0, mo.z, dist, endPos.z)
	
	local diff = {
		x = FixedMul(speed, FixedMul(cos(angle), cos(pitch))), 
		y = FixedMul(speed, FixedMul(sin(angle), cos(pitch))), 
		z = FixedMul(speed, sin(pitch))
	}
	
	nextPos.x = TGFDifferenceCheck(diff.x, nextPos.x, endPos.x)
	nextPos.y = TGFDifferenceCheck(diff.y, nextPos.y, endPos.y)
	nextPos.z = TGFDifferenceCheck(diff.z, nextPos.z, endPos.z)
	
	mo.angle = R_PointToAngle2(0, 0, diff.x, diff.y)
	
	P_TeleportMove(mo, nextPos.x, nextPos.y, nextPos.z)
	
	if mo.x == endPos.x and mo.y == mo.y and mo.z == endPos.z
		return true
	end
end

addHook("MobjThinker", function(mo)
	if not (mo and mo.valid) then return end
	
	// This MobjThinker is only for the gunner's TGF movement
	if mo.health <= 0 or not mo.tgfMovement then return end
	
	// Setup variables for the TGF-based movement
	if not mo.tgfOrigPos
		mo.tgfOrigPos = {
			x = mo.x,
			y = mo.y,
			z = mo.z,
		}
	end
	
	local origPos = mo.tgfOrigPos
	if not mo.tgfPosNum then mo.tgfPosNum = 0 end
	if not mo.tgfReverse then mo.tgfReverse = false end
	if not mo.tgfAngle then mo.tgfAngle = 0 end
	if not mo.tgfPositions
		mo.tgfPositions = {
			{x = origPos.x, y = origPos.y, z = origPos.z},
			{x = origPos.x + FixedMul(cos(mo.tgfAngle), 54*mo.scale),	y = origPos.y + FixedMul(sin(mo.tgfAngle), 54*mo.scale),	z = origPos.z - P_MobjFlip(mo)*29*mo.scale},
			{x = origPos.x + FixedMul(cos(mo.tgfAngle), 121*mo.scale),	y = origPos.y + FixedMul(sin(mo.tgfAngle), 121*mo.scale),	z = origPos.z - P_MobjFlip(mo)*46*mo.scale},
			{x = origPos.x + FixedMul(cos(mo.tgfAngle), 182*mo.scale),	y = origPos.y + FixedMul(sin(mo.tgfAngle), 182*mo.scale),	z = origPos.z - P_MobjFlip(mo)*54*mo.scale},
			{x = origPos.x + FixedMul(cos(mo.tgfAngle), 218*mo.scale),	y = origPos.y + FixedMul(sin(mo.tgfAngle), 218*mo.scale),	z = origPos.z - P_MobjFlip(mo)*41*mo.scale},
			{x = origPos.x + FixedMul(cos(mo.tgfAngle), 252*mo.scale),	y = origPos.y + FixedMul(sin(mo.tgfAngle), 252*mo.scale),	z = origPos.z - P_MobjFlip(mo)*14*mo.scale}
		}
	end
	
	local speed = 6*mo.scale
	
	if mo.tgfPositions and table.maxn(mo.tgfPositions)
		local maxIndex = table.maxn(mo.tgfPositions)
		
		// Move the gunner backwards if it is reversed
		if mo.tgfReverse and mo.tgfPosNum > 1
			if TGFMovement(mo, mo.tgfPositions[mo.tgfPosNum - 1], speed)
				mo.tgfPosNum = $-1
			end
		// Move the gunner forwards if it is not reversed
		elseif not mo.tgfReverse and mo.tgfPosNum < maxIndex
			if TGFMovement(mo, mo.tgfPositions[mo.tgfPosNum + 1], speed)
				mo.tgfPosNum = $+1
			end
		end
		
		// Reverse the gunner's movement if it reaches one of the path's ends
		if (mo.tgfReverse and mo.tgfPosNum <= 1)
		or (not mo.tgfReverse and mo.tgfPosNum >= maxIndex)
			mo.tgfReverse = not $
		end
	end
end, MT_TGFGUNNER)

// TGF Gunner
mobjinfo[MT_TGFGUNNER] = {
		//$Name "TGF Gunner"
		//$Sprite TFBTA1
		//$Category TGF/Concept Enemies
		//$Flags4Text TGF Behavior
        doomednum = 155,
        spawnstate = S_TGFGUNERLOOK1,
        spawnhealth = 1,
        seestate = S_TGFGUNERZOOM1,
        reactiontime = 5,
        attacksound = sfx_s3k4d,
        painsound = sfx_dmpain,
		missilestate = S_TGFGUNERSHOOT1,
        deathstate = S_XPLD_FLICKY,
        deathsound = sfx_pop,
        speed = 1*FRACUNIT,
        radius = 20*FRACUNIT,
        height = 48*FRACUNIT,
		raisestate = MT_JETTBULLET,
        flags = MF_SLIDEME|MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY
}

function A_TGFGunnerShoot(actor, var1, var2)
	if not (actor and actor.valid) then return end
	
	P_SpawnPointMissile(
		actor, actor.x + cos(actor.angle), actor.y + sin(actor.angle), actor.z, 
		actor.info.raisestate, 
		actor.x, actor.y, actor.z
	)
	
	S_StartSound(actor, actor.info.attacksound)
end

states[S_TGFGUNER_TGFMOVEMENT] =	{SPR_TFBT,	A|FF_ANIMATE,	105,	nil,				1,	1,	S_TGFGUNER_SHOOT}
states[S_TGFGUNER_SHOOT] =			{SPR_TFBT,	A,				0,		A_TGFGunnerShoot,	0,	0,	S_TGFGUNER_TGFMOVEMENT}
	
states[S_TGFGUNERLOOK1] = {SPR_TFBT, A, 4, A_Look, 0, 0, S_TGFGUNERLOOK2}       // S_TGFGUNERLOOK1
states[S_TGFGUNERLOOK2] = {SPR_TFBT, B, 4, A_Look, 0, 0, S_TGFGUNERLOOK1}       // S_TGFGUNERLOOK2
states[S_TGFGUNERZOOM1] = {SPR_TFBT, A, 1, A_JetgThink, 0, 0, S_TGFGUNERZOOM2}  // S_TGFGUNERZOOM1
states[S_TGFGUNERZOOM2] = {SPR_TFBT, B, 1, A_JetgThink, 0, 0, S_TGFGUNERZOOM1}  // S_TGFGUNERZOOM2
states[S_TGFGUNERSHOOT1] = {SPR_TFBT, A, 1, A_JETGShoot, 0, 0, S_TGFGUNERSHOOT2} // S_TGFGUNERSHOOT1
states[S_TGFGUNERSHOOT2] = {SPR_TFBT, B, 1, nil, 0, 0, S_TGFGUNERZOOM1}          // S_TGFGUNERSHOOT2
